
/*
 *  
 *
 *  Copyright (C) Microsoft Corporation.  All rights reserved
 *
 *  Databinding - Given a BEC, puts it into an arraylist and makes it ready 
 *  to be bound to .Net conrtrols.
 *  
 */

using System;
using System.Collections.Generic;
using System.Text;

namespace Microsoft.Crm.DataBindining
{
    using System;
    using System.Diagnostics;
    using System.Collections;
    using System.Collections.Specialized;
    using System.ComponentModel;
    using System.Globalization;

    /// <summary>
    /// Databinding. Converting BusinessEntity/DynamicEntity to a indexable UniversalEntity
    /// </summary>
    public class UniversalEntity : BusinessEntity, ICustomTypeDescriptor
    {
        /// <summary>
        /// Recognises the type of property fills in the necessary fields in the property collection.
        /// </summary>
        /// <param name="property"></param>
        /// <param name="dictionary"></param>
        /// <param name="descriptors"></param>
        /// <param name="index"></param>
        private static void FillPropertyDictionaryAndDescriptor(Property property, ref IDictionary dictionary, LocalPropertyDescriptor[] descriptors, ref int index)
        {
            String propertyName = property.Name;
            Type propertyType = property.GetType();

            // String
            if (propertyType == typeof(StringProperty))
            {
                dictionary[propertyName] = ((StringProperty)property).Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));
            }

                // CrmBoolean
            else if (propertyType == typeof(CrmBooleanProperty))
            {
                dictionary[propertyName] = ((CrmBooleanProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(bool));

                dictionary[propertyName + "Name"] = ((CrmBooleanProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
            }

                // CrmDateTime
            else if (propertyType == typeof(CrmDateTimeProperty))
            {
                string dateTimeAsText = ((CrmDateTimeProperty)property).Value.Value;
                DateTime date = DateTime.Parse(dateTimeAsText, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);

                dictionary[propertyName] = date;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(DateTime));

                dictionary[propertyName + "Name"] = date.ToString(); // no need for format provider. as per discussion.
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
            }
                // CrmDecimal
            else if (propertyType == typeof(CrmDecimalProperty))
            {
                dictionary[propertyName] = ((CrmDecimalProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(Decimal));

                dictionary[propertyName + "FormattedValue"] = ((CrmDecimalProperty)property).Value.formattedvalue;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "FormattedValue", typeof(string));
            }

                // CrmFloat
            else if (propertyType == typeof(CrmFloatProperty))
            {
                dictionary[propertyName] = ((CrmFloatProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(float));

                dictionary[propertyName + "FormattedValue"] = ((CrmFloatProperty)property).Value.formattedvalue;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "FormattedValue", typeof(string));
            }

                // CrmMoney
            else if (propertyType == typeof(CrmMoneyProperty))
            {
                dictionary[propertyName] = ((CrmMoneyProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(Decimal));

                dictionary[propertyName + "FormattedValue"] = ((CrmMoneyProperty)property).Value.formattedvalue;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "FormattedValue", typeof(string));
            }

                // CrmNumber
            else if (propertyType == typeof(CrmNumberProperty))
            {
                dictionary[propertyName] = ((CrmNumberProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(int));

                dictionary[propertyName + "FormattedValue"] = ((CrmNumberProperty)property).Value.formattedvalue;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "FormattedValue", typeof(string));
            }

                // EntityNameReference
            else if (propertyType == typeof(EntityNameReferenceProperty))
            {
                dictionary[propertyName] = ((EntityNameReferenceProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));
            }

                // Lookup
            else if (propertyType == typeof(LookupProperty))
            {
                dictionary[propertyName + "Id"] = ((LookupProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Id", typeof(Guid));

                dictionary[propertyName] = ((LookupProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));


                dictionary[propertyName + "Type"] = ((LookupProperty)property).Value.type;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Type", typeof(string));
            }

                // Owner
            else if (propertyType == typeof(OwnerProperty))
            {
                dictionary[propertyName + "Id"] = ((OwnerProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Id", typeof(Guid));

                dictionary[propertyName] = ((OwnerProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));

                dictionary[propertyName + "Type"] = ((OwnerProperty)property).Value.type;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Type", typeof(string));
            }

                // Customer
            else if (propertyType == typeof(CustomerProperty))
            {
                dictionary[propertyName + "Id"] = ((CustomerProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Id", typeof(Guid));

                dictionary[propertyName] = ((CustomerProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));

                dictionary[propertyName + "Type"] = ((CustomerProperty)property).Value.type;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Type", typeof(string));
            }

                // Picklist
            else if (propertyType == typeof(PicklistProperty))
            {
                dictionary[propertyName] = ((PicklistProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(int));

                dictionary[propertyName + "Name"] = ((PicklistProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
            }

                /*
                [Amkumar] I am not able to find this property in CrmSdk
                                // Reference
                            else if(propertyType == typeof(ReferenceProperty))
                            {
                                dictionary[propertyName + "Id"] = ((ReferenceProperty)property).Value.Value;
                                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Id", typeof(Guid));

                                dictionary[propertyName + "Name"] = ((ReferenceProperty)property).Value.name;
                                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
				
                                dictionary[propertyName + "Type"] = ((ReferenceProperty)property).Value.type;
                                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Type", typeof(int));
                            }
                */
            // Status
            else if (propertyType == typeof(StatusProperty))
            {
                dictionary[propertyName] = ((StatusProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(int));

                dictionary[propertyName + "Name"] = ((StatusProperty)property).Value.name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
            }

                // Key
            else if (propertyType == typeof(KeyProperty))
            {
                dictionary[propertyName] = ((KeyProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(Guid));
            }

                // State
            else if (propertyType == typeof(StateProperty))
            {
                dictionary[propertyName] = ((StateProperty)property).Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));

                dictionary[propertyName + "Name"] = ((StateProperty)property).Name;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName + "Name", typeof(string));
            }

                // UniqueIdentifier
            else if (propertyType == typeof(UniqueIdentifierProperty))
            {
                dictionary[propertyName] = ((UniqueIdentifierProperty)property).Value.Value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(Guid));
            }
            else if (propertyType == typeof(DynamicEntityArrayProperty))
            {
                DynamicEntity[] des = ((DynamicEntityArrayProperty)property).Value;
                string value = string.Empty;
                if (des.Length != 0)
                {
                    foreach (DynamicEntity de in des)
                    {
                        IDictionary iDictionary = UniversalEntity.GetDictionaryFromDynamicEntity(de);
                        object lookup = iDictionary["partyid"];
                        if ((lookup != null) && (lookup.GetType() == typeof(string)))
                        {
                            value += lookup + ";\n";
                        }
                    }
                    if (value.Length > 1)
                    {
                        value = value.Substring(0, value.Length - 2);
                    }
                }

                dictionary[propertyName] = value;
                descriptors[index++] = new LocalPropertyDescriptor(propertyName, typeof(string));
            }
        }

        /// <summary>
        /// Given nameToTypeMapping, creates a property descriptor collection from it.
        /// </summary>
        /// <param name="nameToTypeMapping"></param>
        /// <returns></returns>
        public static PropertyDescriptorCollection CreatePropertyDescriptorCollection(IDictionary nameToTypeMapping)
        {
            LocalPropertyDescriptor[] pds = new LocalPropertyDescriptor[nameToTypeMapping.Count];

            int npd = 0;
            foreach (DictionaryEntry de in nameToTypeMapping)
            {
                pds[npd] = new LocalPropertyDescriptor((string)de.Key, (Type)de.Value);
                npd++;
            }

            return new PropertyDescriptorCollection(pds);
        }

        /// <summary>
        /// Create a data-bindable source from a given Business Entity Collection. 
        /// Makes sure that none of the columns specified in nameToTypeMap are absent.
        /// </summary>
        /// <param name="entities"></param>
        /// <param name="nameToTypeMap"></param>
        /// <returns></returns>
        public static ArrayList CreateFromDynamicEntityCollection(BusinessEntityCollection entities, ref IDictionary nameToTypeMap)
        {
            ArrayList list = new ArrayList();
            for (int index = 0; index < entities.BusinessEntities.Length; index++)
            {
                DynamicEntity de = (DynamicEntity)entities.BusinessEntities[index];
                PropertyDescriptorCollection pdc = CreatePropertyDescriptorCollection(nameToTypeMap); 
                UniversalEntity ue = UniversalEntity.CreateFromDynamicEntity(de, pdc);
                list.Add(ue);
            }
            return list;
        }


        /// <summary>
        /// Create a data-bindable source from a given Business Entity Collection. 
        /// If any attribute is "null" it will be missing in the resulting array.
        /// </summary>
        /// <param name="entities"></param>
        /// <returns></returns>
        public static ArrayList CreateFromDynamicEntityCollection(BusinessEntityCollection entities)
        {
            ArrayList list = new ArrayList();
            for (int index = 0; index < entities.BusinessEntities.Length; index++)
            {
                DynamicEntity de = (DynamicEntity)entities.BusinessEntities[index];
                UniversalEntity entity = UniversalEntity.CreateFromDynamicEntity(de);
                list.Add(entity);
            }
            return list;
        }

        /// <summary>
        /// Creates an indexable universal entity from given entity.
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static UniversalEntity CreateFromDynamicEntity(DynamicEntity entity)
        {
            
            IDictionary properties = new HybridDictionary(3 * entity.Properties.Length, false);
            LocalPropertyDescriptor[] pds = new LocalPropertyDescriptor[3 * entity.Properties.Length];

            int index = 0;
            foreach (Property prop in entity.Properties)
            {
                FillPropertyDictionaryAndDescriptor(prop, ref properties, pds, ref index);
            }

            return new UniversalEntity(entity.Name, new PropertyDescriptorCollection(pds), properties);
        }

        /// <summary>
        /// Get dictionary of properties for the given entity
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static IDictionary GetDictionaryFromDynamicEntity(DynamicEntity entity)
        {
            IDictionary properties = new HybridDictionary(3 * entity.Properties.Length, false);
            LocalPropertyDescriptor[] pds = new LocalPropertyDescriptor[3 * entity.Properties.Length];

            int index = 0;
            foreach (Property prop in entity.Properties)
            {
                FillPropertyDictionaryAndDescriptor(prop, ref properties, pds, ref index);
            }

            return properties;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="entity"></param>
        /// <param name="propertyDescriptors"></param>
        /// <returns></returns>
        public static UniversalEntity CreateFromDynamicEntity(
            DynamicEntity entity,
            PropertyDescriptorCollection propertyDescriptors)
        {
            IDictionary properties = new HybridDictionary(3 * entity.Properties.Length, false);
            LocalPropertyDescriptor[] pds = new LocalPropertyDescriptor[3 * entity.Properties.Length];

            int index = 0;
            foreach (Property prop in entity.Properties)
            {
                FillPropertyDictionaryAndDescriptor(prop, ref properties, pds, ref index);
            }

            return new UniversalEntity(entity.Name, propertyDescriptors, properties);
        }
        
        /// <summary>
        /// 
        /// </summary>
        /// <param name="entityName"></param>
        /// <param name="propertyDescriptors"></param>
        /// <param name="propertyValues"></param>
        protected UniversalEntity(
            string entityName,
            PropertyDescriptorCollection propertyDescriptors,
            IDictionary propertyValues)
        {
            _entityName = entityName;
            _propertyDescriptors = propertyDescriptors;
            _properties = propertyValues;
        }

        /// <summary>
        /// Given a property retrieve the value inside that.
        /// </summary>
        /// <param name="prop"></param>
        /// <returns></returns>
        protected static object GetPropertyValue(Property prop)
        {
            Type propertyType = prop.GetType();

            if (propertyType == typeof(StringProperty))
            {
                return ((StringProperty)prop).Value;
            }
            else if (propertyType == typeof(CrmDateTimeProperty))
            {
                string dateTimeAsText = (((CrmDateTimeProperty)prop).Value).Value;
                return DateTime.Parse(dateTimeAsText, CultureInfo.InvariantCulture, DateTimeStyles.AdjustToUniversal);
            }
            else if (propertyType == typeof(KeyProperty))
            {
                return ((KeyProperty)prop).Value;
            }
            else if (propertyType == typeof(LookupProperty))
            {
                return (((LookupProperty)prop).Value).Value;
            }
            else if (propertyType == typeof(StateProperty))
            {
                return ((StateProperty)prop).Value;
            }
            else if (propertyType == typeof(DynamicEntityArrayProperty))
            {
                return "Array of child entities";
            }

            return "Unsupported property type: " + propertyType.GetType().Name;
        }

        private string _entityName;
        private PropertyDescriptorCollection _propertyDescriptors;
        private IDictionary _properties;

        #region ICustomTypeDescriptor Members

        public TypeConverter GetConverter()
        {
            return null;
        }

        public EventDescriptorCollection GetEvents(Attribute[] attributes)
        {
            return null;
        }

        EventDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetEvents()
        {
            return new EventDescriptorCollection(null);
        }

        public string GetComponentName()
        {
            return null;
        }

        public object GetPropertyOwner(PropertyDescriptor pd)
        {
            return this;
        }

        public System.ComponentModel.AttributeCollection GetAttributes()
        {
            return new System.ComponentModel.AttributeCollection(null);
        }

        public PropertyDescriptorCollection GetProperties(Attribute[] attributes)
        {
            return this._propertyDescriptors;
        }

        PropertyDescriptorCollection System.ComponentModel.ICustomTypeDescriptor.GetProperties()
        {
            return GetProperties(null);
        }

        public object GetEditor(Type editorBaseType)
        {
            return null;
        }

        public PropertyDescriptor GetDefaultProperty()
        {
            return null;
        }

        public EventDescriptor GetDefaultEvent()
        {
            return null;
        }

        public string GetClassName()
        {
            return null;
        }

        #endregion

        #region PropertyDescriptor

        private class LocalPropertyDescriptor : PropertyDescriptor
        {
            public LocalPropertyDescriptor(string name, Type valueType)
                : base(name, null)
            {
                _valueType = valueType;
            }

            public override bool CanResetValue(object component)
            {
                return false;
            }

            public override object GetValue(object component)
            {
                object propertyValue = ((UniversalEntity)component)._properties[this.Name];

                return propertyValue != null ? propertyValue : string.Empty; // "Not defined: '" + this.Name + "'";
            }

            public override void ResetValue(object component)
            {
                throw new NotSupportedException();
            }

            public override void SetValue(object component, object value)
            {
                throw new NotSupportedException();
            }

            public override bool ShouldSerializeValue(object component)
            {
                return false;
            }

            public override Type ComponentType { get { return typeof(UniversalEntity); } }

            public override bool IsReadOnly { get { return true; } }

            public override Type PropertyType { get { return _valueType; } }

            private Type _valueType;
        }

        #endregion
    }

}
